﻿namespace InkType.ViewModel

open InkType.ViewModel.SavedTypefaces
open System
open System.Collections.Generic
open System.Collections.ObjectModel
open System.ComponentModel
open System.Windows
open System.Windows.Ink
open System.Windows.Input
open System.Windows.Media


module GlyphViewModelUtil =
    type StrokeCollection with
        member x.ToByteArray () =
            use stream = new IO.MemoryStream ()
            x.Save (stream, true)

            stream.Position <- 0L
            let array = Array.zeroCreate (int stream.Length)
            stream.Read (array, 0, array.Length) |> ignore

            array

        static member FromByteArray (bytes : byte []) =
            use stream = new IO.MemoryStream (bytes)
            StrokeCollection (stream)


open GlyphViewModelUtil

type [<AllowNullLiteral>] GlyphViewModel private (charCode, typefaceParent, strokeCollection, model : GlyphModel option, dc : ModelDataContext) =
    inherit DependencyObject ()

    static let GlyphWidthPropertyKey =
        Dp.regr<float, GlyphViewModel> "GlyphWidth"
            (Dp.Meta (GlyphMetrics.Height))

    let model = ref model

    static member GlyphWidthProperty = GlyphWidthPropertyKey.DependencyProperty

    static member val AdvanceModeProperty =
        Dp.reg<GlyphAdvanceMode, GlyphViewModel> "AdvanceMode"
            (Dp.Meta (
                GlyphAdvanceMode.``Full Width Advance``, fun (x : GlyphViewModel) _ -> x.OnAdvanceModeChanged ()))

    static member val AdvanceModeParamProperty =
        Dp.reg<float, GlyphViewModel> "AdvanceModeParam"
            (Dp.Meta (
                200., fun (x : GlyphViewModel) _ -> x.OnAdvanceModeParamChanged ()))

    new (charcode, typefaceParent, dc : ModelDataContext) =
        GlyphViewModel (charcode, typefaceParent, new StrokeCollection (), None, dc)

    new (model : GlyphModel, dc) as x =
        GlyphViewModel (model.CharCode, model.Typeface, StrokeCollection.FromByteArray (model.StrokeData.ToArray ()), Some model, dc)
        then
            x.AdvanceMode <- enum model.GlyphAdvanceMode
            x.AdvanceModeParam <- model.GlyphAdvanceModeParam

    member x.CharCode = charCode
    member x.Char = char charCode
    member x.CharCodeHex = sprintf "%X" charCode

    member x.StrokeCollection = strokeCollection

    member x.GlyphWidth
        with get () = x.GetValue GlyphViewModel.GlyphWidthProperty :?> float
        and private set (v : float) = x.SetValue (GlyphWidthPropertyKey, box v)

    member x.AdvanceMode
        with get () = x.GetValue GlyphViewModel.AdvanceModeProperty :?> GlyphAdvanceMode
        and set (v : GlyphAdvanceMode) = x.SetValue (GlyphViewModel.AdvanceModeProperty, box v)

    member x.AdvanceModeParam
        with get () = x.GetValue GlyphViewModel.AdvanceModeParamProperty :?> float
        and set (v : float) = x.SetValue (GlyphViewModel.AdvanceModeParamProperty, box v)


    member private x.OnAdvanceModeChanged () =
        x.GlyphWidth <- GlyphAdvanceModes.GetGlyphWidth x.AdvanceMode x.AdvanceModeParam

    member private x.OnAdvanceModeParamChanged () =
        x.GlyphWidth <- GlyphAdvanceModes.GetGlyphWidth x.AdvanceMode x.AdvanceModeParam


    member x.OnExit () =
        let newModel =
            let shouldAddToDatabase =
                x.StrokeCollection.Count > 0
                || x.AdvanceMode <> GlyphAdvanceMode.``Full Width Advance``
                || x.AdvanceModeParam <> 200.

            match shouldAddToDatabase with
            | false ->
                match !model with
                | Some model -> dc.Glyph.DeleteOnSubmit model
                | None -> ()
                None

            | true ->
                let updateModel (model : GlyphModel) =
                    model.GlyphAdvanceMode <- int x.AdvanceMode
                    model.GlyphAdvanceModeParam <- x.AdvanceModeParam
                    model.StrokeData <- Data.Linq.Binary (x.StrokeCollection.ToByteArray ())

                match !model with
                | None ->
                    let model = GlyphModel ()
                    model.CharCode <- charCode
                    model.Typeface <- typefaceParent

                    updateModel model
                    dc.Glyph.InsertOnSubmit model
                    Some model

                | Some model ->
                    updateModel model
                    Some model

        dc.DataContext.SubmitChanges ()
        model := newModel



